Expand description
Proc macro anyhow, a combination of ideas from
anyhow
and
proc-macro-error
to improve proc macro
development, especially focused on the error handling.
§Motivation
Error handling in proc-macros is unideal, as the top level functions of
proc-macros can only return TokenStreams
both in success and failure case.
This means that I often write code like this, moving the actual
implementation in a separate function to be able to use the ergonomic rust
error handling with e.g., ?
.
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
match actual_implementation(input.into()) {
Ok(output) => output,
Err(error) => error.into_compile_error(),
}
.into()
}
fn actual_implementation(input: TokenStream2) -> syn::Result<TokenStream2> {
// ..
}
§Using the #[manyhow]
macro
To activate the error handling, just add #[manyhow]
above any
proc-macro implementation, reducing the above example to:
use manyhow::manyhow;
use proc_macro2::TokenStream as TokenStream2;
#[manyhow]
#[proc_macro]
// You can also merge the two attributes: #[manyhow(proc_macro)]
fn my_macro(input: TokenStream2) -> syn::Result<TokenStream2> {
// ..
}
// On top of the TokenStreams any type that implements [`Parse`] is supported
#[manyhow(proc_macro_derive(MyMacro))]
#[proc_macro]
// The output can also be anything that implements `quote::ToTokens`
fn my_derive_macro(input: syn::DeriveInput) -> manyhow::Result<syn::ItemImpl> {
// ..
}
See Without macros to see what this expands to under the hood.
You can also use the #[manyhow]
attributes on a use statement, useful when
moving your proc-macro implementations in separate modules.
use manyhow::manyhow;
mod module {
use proc_macro2::TokenStream as TokenStream2;
pub fn my_macro(input: TokenStream2) -> syn::Result<TokenStream2> {
// ..
}
}
#[manyhow]
#[proc_macro]
// You can also merge the two attributes: #[manyhow(proc_macro)]
pub use module::my_macro;
A proc macro function marked as #[manyhow]
can take and return any
TokenStream
, and can also return Result<TokenStream, E>
where E
implements ToTokensError
. As additional parameters a
dummy and/or emitter can
be specified.
The manyhow
attribute takes optional flags to configure its behavior.
When used for proc_macro
and proc_macro_attribute
,
#[manyhow(input_as_dummy, ...)]
will take the input of a function like
proc_macro
to initialize the [dummy &mut TokenStream
](#
dummy-mut-tokenstream) while #[manyhow(item_as_dummy, ...)]
on
proc_macro_attribute
will initialize the dummy with the annotated item.
You can merge the #[proc_macro*]
attribute inside the manyhow flags e.g.,
#[manyhow(proc_macro)]
or #[manyhow(proc_macro_derive(SomeTrait, ...))]
.
The #[manyhow(impl_fn, ...)]
flag will put the actual macro implementation
in a separate function. Making it available for e.g., unit testing with
proc_macro_utils::assert_expansion!
.
#[manyhow(impl_fn)]
#[proc_macro]
pub fn actual_macro(input: TokenStream2) -> TokenStream2 {
// ...
}
// would roughly expand to
#[proc_macro]
pub fn actual_macro(input: TokenStream) -> TokenStream {
actual_macro_impl(input.into()).into()
}
fn actual_macro_impl(input: TokenStream2) -> TokenStream2 {
// ...
}
§Without macros
manyhow
can be used without proc macros, and they can be disabled by
adding manyhow
with default-features=false
.
The usage is more or less the same, though with some added boilerplate from
needing to invoke one of function()
(function!
), attribute()
(attribute!
) or derive()
(derive!
) directly. For each version
there exists a function and a macro_rules
macro, while the function only
supports proc_macro::TokenStream
and proc_macro2::TokenStream
, the
macro versions also support any type that implements Parse
and ToTokens
respectively.
While the examples use closures, functions can be passed in as well. The above example would then change to:
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
manyhow::function(
input,
false,
|input: TokenStream2| -> syn::Result<TokenStream2> {
// ..
},
)
// Or
manyhow::function!(
input,
|input: syn::DeriveInput| -> manyhow::Result<syn::ItemImpl> {
// ..
},
)
}
Emitter
and dummy
TokenStream
can also be used. function()
(function!
) and attribute()
(attribute!
) take an additional
boolean parameter controlling whether the input/item will be used as initial
dummy.
§emitter: &mut Emitter
*MacroHandler
s (the traits defining what
closures/functions can be used with manyhow
) can take a mutable reference
to an Emitter
. This allows collecting errors, but not fail immediately.
Emitter::into_result
can be used to return if an Emitter
contains
any values.
use manyhow::{manyhow, Emitter, ErrorMessage};
use proc_macro2::TokenStream as TokenStream2;
#[manyhow]
#[proc_macro]
fn my_macro(input: TokenStream2, emitter: &mut Emitter) -> manyhow::Result<TokenStream2> {
// ..
emitter.emit(ErrorMessage::call_site("A fun error!"));
emitter.into_result()?;
// ..
}
§dummy: &mut TokenStream
*MacroHandler
s can also take a mutable reference
to a TokenStream
, to enable emitting some dummy code to be used in case
the macro errors.
This allows either appending tokens e.g., with ToTokens::to_tokens
or
directly setting the dummy code e.g., *dummy = quote!{some tokens}
.
§Crate features
macros
default Enables#[manyhow]
attribute macro.syn
/syn2
default Enables errors forsyn
2.x.syn1
Enables errors forsyn
1.x.darling
Enables errors fordarling
.
Macros§
- Handles
proc_macro_attribute
implementation - Exit by returning error, matching
anyhow::bail!
. - Handles
proc_macro_derive
implementation. - Push an error to an emitter.
- Return early with an error, if a condition is not satisfied, matching
anyhow::ensure!
. - Creates an
ErrorMessage
, comparable to theanyhow!
macro - Handles function like
proc_macro
implementation
Structs§
- Allows emitting errors without returning.
- This crates Error type
- A single error message
- Error that does not expand to any
compile_error!
and therefor does not cause compilation to fail.
Traits§
- Marker trait for
proc_macro::TokenStream
andproc_macro2::TokenStream
- Exposes
ErrorMessage::attachment
as a trait to allowResultExt::attachment
. - Input of an attribute proc-macro
- Input of a derive proc-macro
- Input of a function proc-macro
- Allows to call
.join(..)
on anyimpl ToTokensError
- Output of a macro handler.
- Some utilities on
Result<T, impl ToTokensError>
- Error that can be converted to a
TokenStream
required to be used withMacroOutput
Functions§
- Handles
proc_macro_attribute
implementation - Handles
proc_macro_derive
implementation. - Handles function like
proc_macro
implementation
Type Aliases§
- An alias for
Result
suited for use with this crate
Attribute Macros§
- manyhow
macros
Attribute macro to remove boilerplate from proc macro entry points.